AWS上にLAMPのBlue-Green Deployment環境を構築する
よく訓練されたアップル信者、都元です。
先週土曜日にJAWS DAYS 2014でお話したCloudFormationセッションの一節で、Blue-Green Deploymentを実現するLAMP環境について触れました。この「Blue-Green Deployment」という言葉の出どころは、私の尊敬する人でもあるMartin Fowler氏の記事 BlueGreenDeployment(2010年) のようです。
Blue-Green Deploymentとは
TODO…じゃなくてw(自分で傷口を抉る)
要するに、2つの独立したアプリ動作環境(要するにサーバ群)を用意して、それぞれをBlue, Greenと名づけます。そして、ここでは現在Blueがライブ(ユーザに提供中の本番環境)であるとします。
リリース時には、Green側に新バージョンのアプリをデプロイして動作確認を行います。問題が無いことが確認でき次第、今までBlueに接続していたユーザをGreenに接続するように切り替え *1ます。その結果、今度はGreenがライブになります。これで、ダウンタイム無しのリリースが実現できます。万一リリース後の新バージョンに何らかの問題が見つかった場合、すぐにライブをBlueに戻せば傷は浅くて済みますね。
ライブではない方(デッド?)の環境は、リリースに問題が無いことが確認でき次第閉じてしまっても構いません。また、次のリリースが直近に控えている場合は、その時に再利用しても構いません。元記事では言及されていませんが、開発環境として利用するのも良いのではないかと思っています。
というのがBlue-Green Deploymentの概要です。
というCloudFormationテンプレートのデモ
で、この環境を一発構築するCloudFormationテンプレートがあります。やってみましょう。ただし、まずは幾つか事前準備が必要です。
事前準備
- EC2キーペアは作っておいてください。まぁ、持ってますよね?
- Route53のHostedZone作成を行い、権威サーバにNSレコードの登録をしておいてください。ここではfoobar.example.comとして話を進めますので、適宜読み替えてください。
- デモ用のアプリケーションを用意してください。(後述)
アプリの要件
この環境にデプロイするPHP(バージョンは5.4)のアプリケーションを作り、tgzで固めます。このアーカイブがサーバ内の/var/www/html/内に展開されますので、そのように意識してアーカイブを作ります。
参照するDB情報は下記のように取得できます。アプリ内にハードコードしないようにしましょう。
- $_SERVER['RDS_HOSTNAME'] : MySQL (RDS) のホスト名
- $_SERVER['RDS_PORT'] : MySQLのポート番号
- $_SERVER['RDS_USERNAME'] : ユーザ名
- $_SERVER['RDS_PASSWORD'] : パスワード
- $_SERVER['RDS_DB_NAME'] : DB名
また、/healthcheck.phpへのリクエストに対してヘルスチェックを行い、問題なければHTTPステータス200を返すことが必要です。例えばこんな感じ?
<?php $dsn = 'mysql:dbname='.$_SERVER['RDS_DB_NAME'].';host='.$_SERVER['RDS_HOSTNAME']; try { $dbh = new PDO($dsn, $_SERVER['RDS_USERNAME'], $_SERVER['RDS_PASSWORD']); if ($dbh == null){ print 'Error'; http_response_code(500); } else { print 'OK'; http_response_code(200); } } catch (PDOException $e) { print('Error:'.$e->getMessage()); http_response_code(500); die(); } $dbh = null;
さらに、リリース作業をデモ体験するために、2種類のバージョンを用意しましょう。tgzアーカイブは後ほど、指定したS3バケットにアップロードするので、手元に持っておいてください。
…ってのはもうメンドクサイですねw もしアプリの内容がどうでもよければ私が用意したサンプルを利用できますので、アプリの準備はパスして頂いても構いません。
初期構築作業
さて、以上の準備が整ったらスタック構築です。構築時は2段階に分けてスタック構築作業をします。上のパラメータに示した通り、まずは Blue, Green 共に台数を 0 として起動を行います。この状態で少々の設定と準備を行い、さらにスタックを更新していきます。
環境構築(1) 〜 VPC, RDS, Bastionの構築
では行きましょう。下記のボタンからスタック作成開始です。
このテンプレートのパラメータは以下の通りです。ちょっと多いですが。このcreate stackの結果、VPC環境、Bastion、RDSが出来上がります。前述の通り、Blue, Green共に台数が 0 であるため、Webサーバはまだ展開されません。
パラメータ名 | 説明 | CREATE時の指定 |
---|---|---|
KeyName | 作成済みのキー名 (ex. ExampleKey) | あなたの環境に合わせて適宜 |
HostedZone | 作成済みのHostedZone名 (ex. foobar.example.com) | あなたの環境に合わせて適宜 |
DeveloperBlock | SSHを許すCIDR表記 (ex. 0.0.0.0/0) | あなたの環境に合わせて適宜 |
DBUsername | DBユーザ名 (ex. admin) | admin |
DBPassword | DBパスワード | お好きに |
DBSnapshotName | DBスナップショット名(必要なければ空欄) | 空欄 |
LiveEnv | ライブ環境の指定 (ex. blue) | blue |
BlueInstanceType | Blue環境のEC2インスタンスタイプ (ex. m1.small) | m1.small |
BlueFleetSize | Blue環境のWebサーバ数 (ex. 4) | 0 |
BlueApplicationURL | Blue環境のアプリケーションアーカイブURL | ひとまず空欄 |
BlueDatabaseName | Blue環境が利用するDB名 | production |
GreenInstanceType | Green環境のEC2インスタンスタイプ (ex. t1.micro) | t1.micro |
GreenFleetSize | Green環境のWebサーバ数 (ex. 1) | 0 |
GreenApplicationURL | Green環境のアプリケーションアーカイブURL | ひとまず空欄 |
GreenDatabaseName | Green環境が利用するDB名 | development |
DB構築
スタックの作成が終わったら、少々の設定を行います。
原理主義的には「SSHしたら負け」なんですが、スタックの作成が終わったら、BastionにSSH接続してみましょう。mysqlコマンドでRDSに接続し、production / development という2つのDBを作ります。ここの自動化はまた別テーマの話題となってしまいますので、何卒ご容赦をw
$ mysql -h rds.local.foobar.example.com -u admin -p password: (適宜入力) mysql> create database production; mysql> create database development; mysql> exit $ exit
アプリケーションアップロード
DB作成が終わったら、次はアプリケーションのアップロードです。自作アプリを準備しない方は、この段落はスキップして次のURLをご利用ください。S3のバケットとして、bgd-lamp-appbucket-XXXXXXXXXXXXXが新規作成されています。このバケットにtgzアーカイブをアップロードし、URLを控えておいてください。上記バケットでなくても、publicにアクセスできる場所であればどこでも(S3でなくても)構いません。
- https://s3-ap-northeast-1.amazonaws.com/cm-public-cfn-sandbox/samplephpapp/v1.tgz
- https://s3-ap-northeast-1.amazonaws.com/cm-public-cfn-sandbox/samplephpapp/v2.tgz
- https://s3-ap-northeast-1.amazonaws.com/cm-public-cfn-sandbox/samplephpapp/v3.tgz
ちなみにこちらから提供するデモアプリの内容はほとんどありません。index.phpとしては、下記のように「hello バージョン名 PDOのデータソース名」を表示するだけです。
<?php $dsn = 'mysql:dbname='.$_SERVER['RDS_DB_NAME'].';host='.$_SERVER['RDS_HOSTNAME']; print 'hello v1 '.$dsn;
環境構築(2) 〜 Webサーバの展開
準備が終わったらWebサーバを展開していきましょう。下記パラメータでスタックの更新を行います。明示しなかったものは変更しません。テンプレートには変更ありませんので「Provide an S3 URL to template」を選択し、テンプレートのURL *2を入力してください。この後の更新作業についても同様です。
パラメータ名 | 現在の値 | 新しい値 |
---|---|---|
BlueFleetSize | 0 | 4 |
BlueApplicationURL | 空欄 | 用意したURL v1 |
GreenFleetSize | 0 | 1 |
GreenApplicationURL | 空欄 | 用意したURL v2 |
このデモでは、現在Blue環境をライブと考えていますので、インスタンスサイズはm1.small、台数は4台としてあります。一方Greenは開発環境と考えていますので、インスタンスサイズは小さくt1.micro、台数も1台としました。また、Blueにはv1を、Greenにはv2をデプロイする設定です。
アップデートが終わりしばらくしたら(ELBのヘルスチェックが通ったら)、下記URLにアクセスしてみましょう。
- http://blue.foobar.example.com/
- http://green.foobar.example.com/
- http://foobar.example.com/
それぞれ、v1・v2・v1 が表示されたと思います。2つのサブドメイン blue / green で環境を指定してアクセスができます。そして、apexドメインでは、現在ライブとなっている環境に接続できます。
開発作業
このように、Blueでサービスを提供し、Greenで開発を進めます。tgzを作ってはS3にアップロードしては、GreenApplicationURLを書き換えてスタックアップデートです。好きなだけ試行錯誤してください。
パラメータ名 | 現在の値 | 新しい値 |
---|---|---|
GreenApplicationURL | 開発中v2のURLその1 | 開発中v2のURLその2 |
リリース作業
アプリの開発が一段落し、この状態のGreenをリリースすることになったとします。
DBの切り替えと、キャパ増強
まず、Greenは今まで開発環境として、開発DB(development)に接続していました。これを本番環境に切り替える必要があります。また、リリースにあたって、Green環境のキャパを本番サイズ(m1.small×4台体制)にスケールさせる必要があります。
上記の作業は2ステップに分けて実行しても構いませんが、ここでは一気にやってしまいましょう。またスタックのアップデートです。
パラメータ名 | 現在の値 | 新しい値 |
---|---|---|
GreenInstanceType | t1.micro | m1.small |
GreenFleetSize | 1 | 4 |
GreenDatabaseName | development | production |
この状態で、リリース前最後のチェックをしてください。
スワップ
確認が終わったらいよいよライブ環境の切り替え(スワップ)です。JAWS DAYS 2014でお見せできたデモは、この操作だけでした。以下のように、LiveEnvをgreenに書き換えてスタックアップデートです。
パラメータ名 | 現在の値 | 新しい値 |
---|---|---|
LiveEnv | blue | green |
以上で、ダウンタイムなくライブ環境の入れ替えができました。
Blue環境の縮退と次期バージョンの開発開始
Green環境が正常に動いていることを確認したら、緊急でBlueに戻すことはもうありませんので、キャパ縮退を行いましょう。また今度はGreenがライブですので、Blueには次期バージョンをデプロイし、接続先DBはdevelopmentに変更しておきましょう。
パラメータ名 | 現在の値 | 新しい値 |
---|---|---|
BlueInstanceType | m1.small | t1.micro |
BlueFleetSize | 4 | 1 |
BlueApplicationURL | v1のURL | v3のURL |
BlueDatabaseName | production | development |
また、もししばらく開発環境も要らないような状況であれば、BlueFleetSizeは 0 にしてしまい、コストを最小化しましょう。
注意点
この構成だと、ライブではない方(デッド?)の環境に対するアクセス制御がされていません。URLさえ分かってしまえば開発環境に誰でもアクセスできてしまいます。この辺りを解決したければ、テンプレートに手をいれる必要があるでしょう。
現状のパラメータに加えて、BlueStatus / GreenStatus 的なものを用意して、public / private みたいな値を設定する感じです。私も実は試してみたんですが、スワップのタイミングで同時にStatus変更を行ってしまうと、数分のライブ環境ダウンタイムが発生してしまいました。ということは、Statusの更新とスワップは、別のステップとして実行しなければならないということです。ちょっと説明が煩雑になるな、と思ってこの機能はひとまず闇に葬りました。まぁ、難しい話じゃないので、チャレンジしてみて頂けると良いかと思います。
まとめ
いかがでしたでしょうか。AWSにおけるLAMPのBlue-Green Deployment環境をご紹介しました。このテンプレートは例によってgithubに上げてありますので、何かありましたらご遠慮なくコントリビュートください。
ちなみに実は、この仕組みがもっとリッチで強力になったものがElastic Beanstalkです。Beanstalkが扱えるのはPHPだけではありません。Java、Node.js、PHP、Python、Ruby、.NETをサポートしています。
今回のようにCloudFormationで構築する場合、ある程度自由に設定ができる一方、テンプレートのメンテナンスが少々厄介かもしれません。Beanstalkはもっと高機能ですが、設定に融通が効かない部分があります。トレードオフですので、上手く選択して頂ければ。